/*
 * Copyright 2010 Aekold Helbrass
 *
 * This file is part of RosterComposer.
 *
 * RosterComposer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * RosterComposer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with RosterComposer.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.heresylabs.rostercomposer;

import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
import org.heresylabs.rostercomposer.roster.EntryNumberListener;
import org.heresylabs.rostercomposer.roster.EntryOptionsListener;
import org.heresylabs.rostercomposer.roster.RosterEntry;
import org.heresylabs.rostercomposer.units.Option;

/**
 *
 * @author Aekold Helbrass <Helbrass@gmail.com>
 */
abstract public class OptionComponent extends JPanel {

    static public OptionComponent createOptionComponent(RosterEntry rosterEntry, Option option) {
        if (option.isLeaf()) {
            if (option.isAnyModels() || option.getMaxNumber() > 1) {
                return new OptionSpinner(rosterEntry, option);
            }
            else {
                return new OptionCheck(rosterEntry, option);
            }
        }

        if (option.getMaxNumber() > 1 || option.isAnyModels() || option.isRepeatable()) {
            return new OptionsGroupLimited(rosterEntry, option);
        }
        return new OptionsGroup(rosterEntry, option);
    }

    private final RosterEntry rosterEntry;
    private final Option option;
    private List<OptionListener> optionListeners;

    public OptionComponent(RosterEntry rosterEntry, Option option) {
        this.rosterEntry = rosterEntry;
        this.option = option;

        if (option.getRequireNumber() > 0) {
            rosterEntry.addNumberListener(new EntryNumberListener() {

                @Override
                public void fireNumberChanged(int number) {
                    if (number < getOption().getRequireNumber()) {
                        deselect();
                        setEnabled(false);
                    }
                    else {
                        setEnabled(true);
                    }
                }

            });
        }
        String conflict = option.getConflict();
        String require = option.getRequire();
        if ((conflict != null && conflict.length() > 0) || (require != null && require.length() > 0)) {
            rosterEntry.addOptionsListener(new EntryOptionsListener() {

                @Override
                public void fireOptionChanged(Option option, boolean added) {
                    checkConflicts(option, added);
                    checkRequirements(option, added);
                }

            });
        }
    }

    final public Option getOption() {
        return option;
    }

    final public RosterEntry getRosterEntry() {
        return rosterEntry;
    }

    final protected String composeTooltip() {
        StringBuilder sb = new StringBuilder();

        sb.append("<html><b>");
        sb.append(option.getName());
        sb.append("</b>");
        int cost = option.getCost();
        if (cost > 0) {
            sb.append(" for ").append(cost);
        }
        int requireNumber = option.getRequireNumber();
        if (requireNumber > 0) {
            sb.append("<br/><i>requires</i> ");
            sb.append(requireNumber);
            sb.append(" <i>models</i>");
        }
        String require = option.getRequire();
        if (require != null && require.length() > 0) {
            sb.append("<br/><i>requires:</i> ");
            sb.append(require);
        }
        String conflict = option.getConflict();
        if (conflict != null && conflict.length() > 0) {
            sb.append("<br/><i>conflicts:</i> ");
            sb.append(conflict);
        }

        return sb.toString();
    }

    abstract public void select();

    abstract public void deselect();

    final public void addOptionListener(OptionListener l) {
        if (optionListeners == null) {
            optionListeners = new ArrayList<OptionListener>();
        }
        optionListeners.add(l);
    }

    final protected void fireOptionChanged(boolean selected) {
        if (optionListeners == null || optionListeners.isEmpty()) {
            return;
        }
        for (int i = 0; i < optionListeners.size(); i++) {
            OptionListener l = optionListeners.get(i);
            l.fireOptionChanged(option, selected);
        }
    }

    protected void checkEnabled() {
        if (option.getRequireNumber() > 0 && rosterEntry.getNumber() < option.getRequireNumber()) {
            setEnabled(false);
            return;
        }
        {
            String conflict = option.getConflict();
            if (conflict != null && conflict.length() > 0) {
                String[] c = conflict.split(";");
                for (int i = 0; i < c.length; i++) {
                    if (rosterEntry.hasOption(c[i])) {
                        setEnabled(false);
                        return;
                    }
                }
            }
        }
        {
            String require = option.getRequire();
            if (require != null && require.length() > 0) {
                String[] r = require.split(";");
                for (int i = 0; i < r.length; i++) {
                    if (!rosterEntry.hasOption(r[i])) {
                        setEnabled(false);
                        return;
                    }
                }
            }
        }
    }

    private void checkConflicts(Option option, boolean added) {
        String conflict = getOption().getConflict();
        if (conflict == null || conflict.isEmpty()) {
            return;
        }
        // checking if we have any conflicts:
        if (conflict.contains(option.getName())) {
            // yes we do
            if (added) {
                // if conflicing option added to entry - we should disable self:
                deselect();
                setEnabled(false);
            }
            else {
                // if conflicting option removed from entry - we should enable self:
                setEnabled(true);
            }
        }
    }

    private void checkRequirements(Option option, boolean added) {
        String require = getOption().getRequire();
        if (require == null || require.isEmpty()) {
            return;
        }
        // do we have some requirements for current option?:
        if (require.contains(option.getName())) {
            if (added) {
                // if requirement was added - we should enable self:
                setEnabled(true);
            }
            else {
                // if requirement removed - disable:
                deselect();
                setEnabled(false);
            }
        }
    }

}
